<i18n locale="en" lang="yaml">
randomIDString: bat-{Random ID}

parameterHint: 'When the parameter value is specified as "INPUT_BY_CALLER", this parameter is allowed to be specified by the caller'

recentTaskCount: 'recent {n} task | recent {n} tasks'
</i18n>

<i18n locale="zh-CN" lang="yaml">
randomIDString: bat-{随机 ID}

Add Async API  : 添加异步 API
Setup Async API: 修改异步 API

Customize ID: 定制 ID
Execute     : 执行
Arguments   : 参数指定
Task Record : 任务记录
Keep        : 保留
Tags        : 标签
Add Tag     : 添加标签
Note        : 备注

URL Preview: URL预览
ID will be a part of the calling URL: 此 ID 用于生成调用时的 URL
'JSON formated arguments (**kwargs)': 'JSON 格式的参数（**kwargs）'
The Func accepts extra arguments not listed above: 本函数允许传递额外的自定义函数参数

'ID must starts with "{prefix}"': 'ID 必须以"{prefix}"开头'
'Only numbers, alphabets, dot(.), underscore(_) and hyphen(-) are allowed': 只能输入数字、英文、点（.）、下划线（_）以及连字符（-）
Please select Func: 请选择执行函数
'Please input arguments, input "{}" when no argument': '请输入参数，无参数时填写 "{}"'

Async API created: 异步 API 已创建
Async API saved  : 异步 API 已保存
Async API deleted: 异步 API 已删除

Are you sure you want to delete the Async API?: 是否确认删除此异步 API？
Invalid argument format: 参数格式不正确

parameterHint: '参数值指定为 "INPUT_BY_CALLER" 时表示允许调用时指定本参数'

recentTaskCount: '{n} 个近期任务'
</i18n>

<!-- Generated by OpenCC START -->
<i18n locale="zh-HK" lang="yaml">
Add Async API: 添加異步 API
Add Tag: 添加標籤
Are you sure you want to delete the Async API?: 是否確認刪除此異步 API？
Arguments: 參數指定
Async API created: 異步 API 已創建
Async API deleted: 異步 API 已刪除
Async API saved: 異步 API 已保存
Customize ID: 定製 ID
Execute: 執行
ID must starts with "{prefix}": ID 必須以"{prefix}"開頭
ID will be a part of the calling URL: 此 ID 用於生成調用時的 URL
Invalid argument format: 參數格式不正確
JSON formated arguments (**kwargs): JSON 格式的參數（**kwargs）
Keep: 保留
Note: 備註
Only numbers, alphabets, dot(.), underscore(_) and hyphen(-) are allowed: 只能輸入數字、英文、點（.）、下劃線（_）以及連字符（-）
Please input arguments, input "{}" when no argument: 請輸入參數，無參數時填寫 "{}"
Please select Func: 請選擇執行函數
Setup Async API: 修改異步 API
Tags: 標籤
Task Record: 任務記錄
The Func accepts extra arguments not listed above: 本函數允許傳遞額外的自定義函數參數
URL Preview: URL預覽
parameterHint: 參數值指定為 "INPUT_BY_CALLER" 時表示允許調用時指定本參數
randomIDString: bat-{隨機 ID}
recentTaskCount: '{n} 個近期任務'
</i18n>
<i18n locale="zh-TW" lang="yaml">
Add Async API: 新增非同步 API
Add Tag: 新增標籤
Are you sure you want to delete the Async API?: 是否確認刪除此非同步 API？
Arguments: 引數指定
Async API created: 非同步 API 已建立
Async API deleted: 非同步 API 已刪除
Async API saved: 非同步 API 已儲存
Customize ID: 定製 ID
Execute: 執行
ID must starts with "{prefix}": ID 必須以"{prefix}"開頭
ID will be a part of the calling URL: 此 ID 用於生成呼叫時的 URL
Invalid argument format: 引數格式不正確
JSON formated arguments (**kwargs): JSON 格式的引數（**kwargs）
Keep: 保留
Note: 備註
Only numbers, alphabets, dot(.), underscore(_) and hyphen(-) are allowed: 只能輸入數字、英文、點（.）、下劃線（_）以及連字元（-）
Please input arguments, input "{}" when no argument: 請輸入引數，無引數時填寫 "{}"
Please select Func: 請選擇執行函式
Setup Async API: 修改非同步 API
Tags: 標籤
Task Record: 任務記錄
The Func accepts extra arguments not listed above: 本函式允許傳遞額外的自定義函式引數
URL Preview: URL預覽
parameterHint: 引數值指定為 "INPUT_BY_CALLER" 時表示允許呼叫時指定本引數
randomIDString: bat-{隨機 ID}
recentTaskCount: '{n} 個近期任務'
</i18n>
<!-- Generated by OpenCC END -->

<template>
  <el-dialog
    id="ScriptSetSetup"
    :visible.sync="show"
    :close-on-click-modal="false"
    :close-on-press-escape="false"
    width="750px">

    <template slot="title">
      {{ pageTitle }} <code class="text-main">{{ data.func_title }}</code>
    </template>

    <el-container direction="vertical">
      <el-main>
        <div class="setup-form">
          <el-form ref="form" label-width="135px" :model="form" :rules="formRules">
            <el-form-item :label="$t('Customize ID')" prop="useCustomId" v-if="pageMode === 'add'">
              <el-switch v-model="useCustomId"></el-switch>
              <span class="text-main float-right">
                {{ $t('URL Preview') }}{{ $t(':') }}
                <code class="code-font">{{ `/api/v1/async/${useCustomId ? form.id : $t('randomIDString')}` }}</code>
              </span>
            </el-form-item>

            <el-form-item label="ID" prop="id" v-show="useCustomId" v-if="pageMode === 'add'">
              <el-input
                maxlength="60"
                v-model="form.id">
              </el-input>
              <InfoBlock :title="$t('ID will be a part of the calling URL')" />
            </el-form-item>

            <el-form-item :label="$t('Execute')" prop="funcId">
              <el-cascader ref="funcCascader"
                popper-class="code-font"
                placeholder="--"
                filterable
                :filter-method="common.funcCascaderFilter"
                v-model="form.funcId"
                :options="funcCascader"
                :props="{ expandTrigger: 'hover', emitPath: false, multiple: false }"
                @change="autoFillFuncCallKwargsJSON">
                </el-cascader>
            </el-form-item>

            <el-form-item :label="$t('Arguments')" prop="funcCallKwargsJSON">
              <el-input
                type="textarea"
                v-model="form.funcCallKwargsJSON"
                resize="none"
                :autosize="{ minRows: 2 }"></el-input>
              <InfoBlock :title="$t('JSON formated arguments (**kwargs)')" />
              <InfoBlock :title="$t('parameterHint')" />

              <InfoBlock v-if="apiCustomKwargsSupport" type="success" :title="$t('The Func accepts extra arguments not listed above')" />
            </el-form-item>

            <el-form-item :label="$t('Tags')" prop="tagsJSON">
              <el-tag v-for="t in form.tagsJSON" :key="t" type="warning" size="small" closable @close="removeTag(t)">{{ t }}</el-tag>
              <el-input v-if="showAddTag" ref="newTag"
                v-model="newTag"
                size="mini"
                @keyup.enter.native="addTag"
                @blur="addTag"></el-input>
              <el-button v-else
                type="text"
                @click="openAddTagInput">{{ $t('Add Tag') }}</el-button>
            </el-form-item>

            <el-form-item :label="$t('Task Record')">
              <span class="task-record-limit-prefix">{{ $t('Keep') }} </span>
              <el-input-number class="task-record-limit-input"
                :min="$store.getters.SYSTEM_INFO('_TASK_RECORD_LIMIT_MIN')"
                :max="$store.getters.SYSTEM_INFO('_TASK_RECORD_LIMIT_MAX')"
                :step="10"
                :precision="0"
                v-model="form.taskRecordLimit"></el-input-number>
              <span class="task-record-limit-unit">{{ $tc('recentTaskCount', form.taskRecordLimit, { n: '' }) }} </span>
              <el-link class="right-button" type="primary" @click.stop="form.taskRecordLimit = $store.getters.SYSTEM_INFO('_TASK_RECORD_FUNC_LIMIT_ASYNC_API')">{{ $t('Restore Default') }}</el-link>
            </el-form-item>

            <el-form-item :label="$t('API Auth')" prop="apiAuthId">
              <el-select v-model="form.apiAuthId" clearable>
                <el-option :label="$t('None')" :value="null"></el-option>
                <el-option v-for="opt in apiAuthList" :label="opt.label" :key="opt.id" :value="opt.id"></el-option>
              </el-select>
            </el-form-item>

            <el-form-item :label="$t('Note')">
              <el-input :placeholder="$t('Optional')"
                type="textarea"
                resize="none"
                :autosize="{minRows: 2}"
                maxlength="5000"
                v-model="form.note"></el-input>
            </el-form-item>

            <el-form-item class="setup-footer">
              <el-link type="danger" class="float-left" v-if="pageMode === 'setup'" @click="deleteData">{{ $t('Delete') }}</el-link>
              <el-button type="primary" v-prevent-re-click @click="submitData">{{ $t('Save') }}</el-button>
            </el-form-item>
          </el-form>
        </div>
      </el-main>
    </el-container>
  </el-dialog>
</template>

<script>
export default {
  name: 'AsyncAPISetup',
  components: {
  },
  watch: {
    show(val) {
      if (val && this.$refs.form) {
        this.$refs.form.clearValidate();
      }

      if (!val) {
        this.$root.$emit('reload.asyncAPIList');
      }
    },
    useCustomId(val) {
      if (val) {
        this.form.id = `my-async-api`;
      } else {
        this.form.id = null;
      }
    },
  },
  methods: {
    async loadData(id) {
      if (!id) {
        this.pageMode = 'add';
        this.T.jsonClear(this.form);
        this.form.taskRecordLimit = this.$store.getters.SYSTEM_INFO('_TASK_RECORD_FUNC_LIMIT_ASYNC_API');
        this.data = {};

      } else {
        this.pageMode = 'setup';
        this.data.id = id;

        let apiRes = await this.T.callAPI_getOne('/api/v1/async-apis/do/list', this.data.id);
        if (!apiRes || !apiRes.ok) return;

        this.data = apiRes.data;

        let nextForm = {};
        Object.keys(this.form).forEach(f => nextForm[f] = this.data[f]);
        nextForm.funcCallKwargsJSON = JSON.stringify(nextForm.funcCallKwargsJSON, null, 2);
        nextForm.tagsJSON  = nextForm.tagsJSON || [];
        nextForm.apiAuthId = this.data.apia_id;

        this.form = nextForm;
      }

      // Get Func list
      let funcList = await this.common.getFuncList();

      this.funcMap      = funcList.map;
      this.funcCascader = funcList.cascader;

      // Get API Auth list
      let apiAuthList = await this.common.getAPIAuthList();

      this.apiAuthList = apiAuthList;

      this.show = true;
    },
    async submitData() {
      try {
        await this.$refs.form.validate();
      } catch(err) {
        return console.error(err);
      }

      switch(this.pageMode) {
        case 'add':
          return await this.addData();
        case 'setup':
          return await this.modifyData();
      }
    },
    async addData() {
      let _formData = this.T.jsonCopy(this.form);
      let opt = {
        body    : { data: _formData },
        feedback: { okMessage: this.$t('Async API created') },
      };

      // Add Func calling kwargs
      try {
        opt.body.data.funcCallKwargsJSON = JSON.parse(this.form.funcCallKwargsJSON);
      } catch(err) {
        return this.T.alert(`${this.$t('Invalid argument format')}<br>${err.toString()}`);
      }

      let apiRes = await this.T.callAPI('post', '/api/v1/async-apis/do/add', opt);
      if (!apiRes || !apiRes.ok) return;

      this.$store.commit('updateHighlightedTableDataId', apiRes.data.id);
      this.show = false;
    },
    async modifyData() {
      let _formData = this.T.jsonCopy(this.form);
      delete _formData.id;

      let opt = {
        params  : { id: this.data.id },
        body    : { data: _formData },
        feedback: { okMessage: this.$t('Async API saved') },
      };

      // Add Func calling kwargsJSON
      try {
        opt.body.data.funcCallKwargsJSON = JSON.parse(this.form.funcCallKwargsJSON);
      } catch(err) {
        return this.T.alert(`${this.$t('Invalid argument format')}<br>${err.toString()}`);
      }

      let apiRes = await this.T.callAPI('post', '/api/v1/async-apis/:id/do/modify', opt);
      if (!apiRes || !apiRes.ok) return;

      this.$store.commit('updateHighlightedTableDataId', apiRes.data.id);
      this.show = false;
    },
    async deleteData() {
      if (!await this.T.confirm(this.$t(`Are you sure you want to delete the Async API?`))) return;

      let apiRes = await this.T.callAPI('/api/v1/async-apis/:id/do/delete', {
        params  : { id: this.data.id },
        feedback: { okMessage: this.$t('Async API deleted') },
      });
      if (!apiRes || !apiRes.ok) return;

      this.show = false;
    },
    autoFillFuncCallKwargsJSON(funcId) {
      // Fill Func calling kwargs automatically
      let parameters = this.funcMap[funcId].argsJSON
                    || Object.keys(this.funcMap[funcId].kwargsJSON);

      let example = {};
      parameters.forEach(p => {
        if (p.indexOf('**') === 0) {
          // TEMP: skip **kwargs parameter
        } else {
          example[p] = 'INPUT_BY_CALLER';
        }
      });

      this.form.funcCallKwargsJSON = JSON.stringify(example, null, 2);
    },
    removeTag(tag) {
      this.form.tagsJSON.splice(this.form.tagsJSON.indexOf(tag), 1);
    },
    openAddTagInput() {
      this.showAddTag = true;
      this.$nextTick(_ => {
        this.$refs.newTag.$refs.input.focus();
      });
    },
    addTag() {
      let newTag = this.newTag;
      if (newTag) {
        if (!Array.isArray(this.form.tagsJSON)) {
          this.$set(this.form, 'tagsJSON', []);
        }
        this.form.tagsJSON.push(newTag);
      }
      this.showAddTag = false;
      this.newTag     = '';
    },
  },
  computed: {
    pageTitle() {
      const _map = {
        setup: this.$t('Setup Async API'),
        add  : this.$t('Add Async API'),
      };
      return _map[this.pageMode];
    },
    apiCustomKwargsSupport() {
      let funcId = this.form.funcId;
      if (!funcId) return false;
      if (!this.funcMap[funcId]) return false;

      for (let k in this.funcMap[funcId].kwargsJSON) {
        if (k.indexOf('**') === 0) return true;
      }
      return false;
    },
  },
  data() {
    let errorMessage_funcCallKwargsJSON = this.$t('Please input arguments, input "{}" when no argument');

    return {
      show    : false,
      pageMode: null,

      data        : {},
      funcMap     : {},
      funcCascader: [],
      apiAuthList : [],

      useCustomId: false,
      showAddTag : false,
      newTag     : '',

      form: {
        id                : null,
        funcId            : null,
        funcCallKwargsJSON: null,
        tagsJSON          : [],
        apiAuthId         : null,
        taskRecordLimit   : this.$store.getters.SYSTEM_INFO('_TASK_RECORD_FUNC_LIMIT_ASYNC_API'),
        note              : null,
      },
      formRules: {
        id: [
          {
            trigger: 'change',
            validator: (rule, value, callback) => {
              if (this.T.notNothing(value)) {
                if (!value.match(/^[0-9a-zA-Z\.\-\_]+$/g)) {
                  return callback(new Error(this.$t('Only numbers, alphabets, dot(.), underscore(_) and hyphen(-) are allowed')));
                }
              }
              return callback();
            },
          }
        ],
        funcId: [
          {
            trigger : 'blur',
            message : this.$t('Please select Func'),
            required: true,
          },
        ],
        funcCallKwargsJSON: [
          {
            trigger : 'blur',
            message : errorMessage_funcCallKwargsJSON,
            required: true,
          },
          {
            trigger  : 'change',
            validator: (rule, value, callback) => {
              try {
                if (value) {
                  let j = JSON.parse(value);
                  if (Array.isArray(j)) {
                    return callback(new Error(errorMessage_funcCallKwargsJSON));
                  }
                }
                return callback();

              } catch(err) {
                return callback(new Error(errorMessage_funcCallKwargsJSON));
              }
            },
          }
        ],
      },
    }
  },
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.task-record-limit-input {
  width: 180px;
}
.task-record-limit-prefix {
  color: grey;
  padding-right: 10px;
}
.task-record-limit-unit {
  color: grey;
  padding-left: 10px;
}
.task-record-limit-unit > span {
  width: 35px;
  display: inline-block;
}
</style>
